import script_lib
from script_lib import checked_frame_apply_script, filled_frame_apply_script, frame_process_restrain, only_frame_do_for_apply_decorator, return_frame_if_exists
from typing import Callable, TYPE_CHECKING, Union
from frame_structure import Frame, ElementSchema, Ontology
from shared_enum import Layer, Process
from visual_structure import VisualStructure
from uuid import uuid4

if TYPE_CHECKING:
    from frame_manager import FrameManager
    from neo_manager import NeoManager

#region 状态附加
@return_frame_if_exists(Layer.Primary)
def zhuangtaifujia_primary(value: Frame) -> Frame:
    frame_manager = value.frame_manager
    _frame = Frame(frame_manager, '基础状态附加')
    script_lib.copy_meta_data_to_new_frame(value, _frame)
    _frame.layer = Layer.Primary
    for element_name, element_schema in frame_manager.frame_name_map['基础状态附加']['elements'].items():
        _frame.elements[element_name] = element_schema.create_empty()

    script_lib.copy_value_to_new_element_container(value.elements['状态'], _frame.elements['状态'])

    fujia = value.elements['附加'].value
    fujia_is_fucus = value.elements['附加'].is_focus
    _frame.elements['附加组合'].add(fujia, fujia_is_fucus)
    while(isinstance(fujia, Frame) and fujia.name == '状态附加'):
        fujia_is_fucus = fujia.elements['附加'].is_focus
        fujia = fujia.elements['附加'].value
        _frame.elements['附加组合'].add(fujia, fujia_is_fucus)
    if len(_frame.elements['附加组合'].focus_items):
        _frame.elements['附加组合'].is_focus = True

    for element_name, element in _frame.elements.items():
        _frame.elements[element_name] = element.apply(checked_frame_apply_script('primary'))
    _frame._process = Process.Checked
    frame_manager.marker_map_primary[_frame.uuid] = _frame
    return _frame

#endregion

#region 基础状态附加
@return_frame_if_exists(Layer.Prototype)
def jichuzhuangtaifujia_prototype(value: Frame) -> Union[Frame, Ontology]:
    frame_manager = value.frame_manager
    _frame = Frame(frame_manager, '原型状态附加')
    script_lib.copy_meta_data_to_new_frame(value, _frame)
    _frame.layer = Layer.Prototype
    for element_name, element_schema in frame_manager.frame_name_map['基础状态附加']['elements'].items():
        _frame.elements[element_name] = element_schema.create_empty()
    
    script_lib.copy_value_to_new_element_container(value.elements['状态'], _frame.elements['状态'])
    script_lib.copy_value_to_new_element_container(value.elements['附加组合'], _frame.elements['附加组合'])

    _elements = {}
    for element_name, element in _frame.elements.items():
        _element = element.apply(checked_frame_apply_script('prototype'), lambda x, y: y is True)
        if _element is not None:
            _elements[element_name] = _element
    _frame.elements = _elements

    res = script_lib.if_only_one_ontology_exist_in_frame(_frame)
    if res[0]:
        frame_manager.marker_map_prototype[_frame.uuid] = res[1]
        return res[1]
    else:
        _frame._process = Process.Checked
        frame_manager.marker_map_prototype[_frame.uuid] = _frame
        return _frame

#endregion

#region 原型状态附加
def yuanxingzhuangtaifujia_visual(frame: Frame) -> VisualStructure:
    vs = VisualStructure()
    fujia_str: list[str] = []
    for value in frame.elements['附加组合'].value:
        if isinstance(value, Ontology):
            fujia_str.append(value.name)
        else:
            fujia_str.append(value.script['visual'].to_string())
    zhuangtai = frame.elements['状态'].value
    if isinstance(zhuangtai, Ontology):
        zhuangtai_str = zhuangtai.name
    else:
        zhuangtai_str = zhuangtai.script['visual'].to_string()
    vs.str = ','.join(fujia_str) + '的' + zhuangtai_str
    return vs

#endregion

#region 形成原型

def xingchengyuanxing_check(value: Frame) -> bool:
    script_lib.common_check(value)
    if not isinstance(value.elements['概念'].value, Ontology):
        value._process = Process.Filled
        return False
    else:
        return True

def xingchengyuanxing_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.append_node(0, value.elements['概念'].value.name)
    miaoshu = value.elements['描述']
    if isinstance(miaoshu, Ontology):
        miaoshu_str = miaoshu.name
    else:
        miaoshu_str = miaoshu.script['visual'](miaoshu).to_string()
    vs.append_node(1, miaoshu_str)
    vs.edges[(1, 0), '形成原型']
    return vs

#endregion

#region 运动事件

def yundongshijian_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.str = '{0}{1}运动后{2}'.format(
        script_lib.get_string_from_element(value, '动体'),
        script_lib.get_string_from_element(value, '方向'),
        script_lib.get_string_from_element(value, '运动结束后状态')
    )
    return vs
#endregion

#region 中药
def zhongyao_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.str = ','.join([
        script_lib.get_string_from_element(value, '中药'),
        script_lib.get_string_from_element(value, '用量'),
        script_lib.get_string_from_element(value, '炮制'),
        script_lib.get_string_from_element(value, '用法')
    ])
    return vs
#endregion

#region 状态组合
def zhuangtaizuhe_prototype(value: Frame) -> Union[Frame, Ontology]:
    _frame = script_lib.common_to_prototype(value)
    if '弱聚焦状态组合' in _frame.elements:
        del(_frame.elements['弱聚焦状态组合'])
    res = script_lib.if_only_one_ontology_exist_in_frame(_frame)
    if res[0]:
        _frame.frame_manager.marker_map_prototype[_frame.uuid] = res[1]
        return res[1]
    else:
        return _frame

def zhuangtaizuhe_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    _str_list = []
    for element_name in list(value.elements):
        _str = script_lib.get_string_from_element(value, element_name)
        if _str != '':
            _str_list.append(_str)
    vs.str = ','.join(_str_list)
    return vs
#endregion

#region 方剂
def get_herb_name(value: Union[Frame, Ontology]) -> str:
    if isinstance(value, Frame):
        return value.elements['中药'].value.name
    else:
        return value.name

def fangji_generate(value: Frame) -> Frame:
    if hasattr(value, 'is_generated') and value.is_generated:
        return value
    if value.elements['基础药物组合'].value != set():
        if '制备' in value.elements:
            for herb in value.elements['基础药物组合'].value:
                if isinstance(herb, Frame):
                    herb.elements['制备'] = value.elements['制备'] 
                    if '每次服药量' in value.elements:
                        if '备注' in herb.elements:
                            herb.elements['备注'].value += value.elements['每次服药量'].value
                        else:
                            herb.elements['备注'] = value.elements['每次服药量']

            if '制备后加入中药' in value.elements:
                zbhjrzy_element = value.elements['制备后加入中药']
                for herb in zbhjrzy_element.value:
                    value.elements['基础药物组合'].add(herb, herb in zbhjrzy_element.focus_items)

        if '是否有加减' in value.elements and value.elements['是否有加减'].value:
            value.elements['加减后药物组合'] = value.frame_manager.frame_name_map['方剂']['elements']['加减后药物组合'].create_empty()
            jjhywzh = value.elements['加减后药物组合']

            jcywzh = value.elements['基础药物组合']
            for herb in jcywzh.value:
                jjhywzh.add(herb, False)

            if '减少中药' in value.elements:
                jszy = value.elements['减少中药']
                remove_from_value = set()
                for herb in jszy.value:
                    herb_name = get_herb_name(herb)
                    for original_herb in jjhywzh.value:
                        if herb_name == get_herb_name(original_herb):
                            remove_from_value.add(original_herb)
                jjhywzh.value = jjhywzh.value - remove_from_value

            if '增加中药' in value.elements:
                zjzy = value.elements['增加中药']
                for herb in zjzy.value:
                    jjhywzh.add(herb, False)

            jjhywzh.is_focus = False
            jjhywzh._process = Process.Checked
    value.is_generated = True
    return value

def fangji_prototype(value: Frame) -> Union[Frame, Ontology]:
    _frame = script_lib.common_to_prototype(value)
    jcywzh_empty = False
    if '基础药物组合' in _frame.elements:
        if _frame.elements['基础药物组合'].value == set():
            jcywzh_empty = True
            count = 2
    else:
        jcywzh_empty = True
        count = 1
    if jcywzh_empty:
        if len(_frame.elements) == count and '方剂名称' in _frame.elements:
            fangji = _frame.elements['方剂名称'].value
            _frame.frame_manager.marker_map_prototype[_frame.uuid] = fangji
            return fangji
    return _frame

def fangji_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    base_herbs_str = script_lib.get_string_from_element(value, '基础药物组合')
    fangji_name = script_lib.get_string_from_element(value, '方剂名称')
    add_herbs = script_lib.get_string_from_element(value, '增加中药')
    remove_herbs = script_lib.get_string_from_element(value, '减少中药')
    if fangji_name:
        base_str = ' '.join([fangji_name, base_herbs_str])
    else:
        base_str = base_herbs_str
    if add_herbs:
        base_str += '加' + add_herbs
    if remove_herbs:
        base_str += '减' + remove_herbs
    vs.str = base_str
    return vs

#endregion

#region 一般过程
def yibanguocheng_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()

    qzt = script_lib.get_string_from_element(value, '前状态')
    vs.append_node(0, qzt)

    hzt = script_lib.get_string_from_element(value, '后状态')
    vs.append_node(1, hzt)

    gy = script_lib.get_string_from_element(value, '干预')
    brx = script_lib.get_string_from_element(value, '发生必然性')
    if gy != '' and brx != '':
        _str = f'{gy}后{brx}'
    else:
        _str = gy + brx
    vs.edges[(0, 1)] = _str
    vs.str = f'{qzt}{_str}发生{hzt}'
    return vs
#endregion

#region 治疗
@return_frame_if_exists(Layer.Primary)
def zhiliao_primary(value: Frame) -> Frame:
    frame_manager = value.frame_manager
    _frame = Frame(frame_manager, '一般过程')
    script_lib.copy_meta_data_to_new_frame(value, _frame)
    _frame.layer = Layer.Primary
    for element_name, element_schema in frame_manager.frame_name_map['一般过程']['elements'].items():
        _frame.elements[element_name] = element_schema.create_empty()
    script_lib.copy_value_to_new_element_container(value.elements['前状态'], _frame.elements['前状态'])

    if '发生必然性' in value.elements:
        script_lib.copy_value_to_new_element_container(value.elements['发生必然性'], _frame.elements['发生必然性'])
    else:
        del(_frame.elements['发生必然性'])
    
    if '后状态' in value.elements:
        script_lib.copy_value_to_new_element_container(value.elements['后状态'], _frame.elements['后状态'])
    else:
        if '健康' in frame_manager.marker_map:
            jiankang = frame_manager.marker_map['健康']
        else:
            jiankang = Ontology(frame_manager, '健康')
        hzt = _frame.elements['后状态']
        hzt.value = jiankang
        hzt.is_focus = True

    _syfj = Frame(frame_manager, '使用方剂')
    script_lib.copy_meta_data_to_new_frame(value, _syfj)
    _syfj.uuid = uuid4().hex
    _syfj.layer = Layer.Primary
    _syfj.elements['方剂'] = frame_manager.frame_name_map['使用方剂']['elements']['方剂'].create_empty()
    script_lib.copy_value_to_new_element_container(value.elements['治疗方法'], _syfj.elements['方剂'])
    _syfj.elements['方剂'] = _syfj.elements['方剂'].apply(checked_frame_apply_script('primary'))
    _syfj.elements['方剂'].is_focus = True
    _syfj._process = Process.Checked
    frame_manager.marker_map_primary[_syfj.uuid] = _syfj
    _frame.elements['干预'].value = _syfj
    _frame.elements['干预'].is_focus = True
    
    for element_name, element in _frame.elements.items():
        _frame.elements[element_name] = element.apply(checked_frame_apply_script('primary'))
    _frame._process = Process.Checked
    frame_manager.marker_map_primary[_frame.uuid] = _frame
    return _frame
#endregion

#region 使用方剂
def shiyongfangji_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.str = script_lib.get_string_from_element(value, '方剂')
    return vs
#endregion

#region 状态加减
def zhuangtaijiajian_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    yzt = script_lib.get_string_from_element(value, '原状态')
    zj = script_lib.get_string_from_element(value, '增加状态')
    js = script_lib.get_string_from_element(value, '减少状态')
    if zj:
        zj = ',合并' + zj
    if js:
        js = ',无' + js
    vs.str = yzt + vs.str + js
    return vs
#endregion

#region 两者相关
def liangzhexiangguan_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    qz = script_lib.get_string_from_element(value, '前者')
    hz = script_lib.get_string_from_element(value, '后者')
    lx = script_lib.get_string_from_element(value, '类型')
    vs.append_node(0, qz)
    vs.append_node(1, hz)
    vs.edges[(1, 0)] = lx
    if lx:
        _str = f'{qz}是{hz}{lx}'
    else:
        _str = f'{qz},{hz}相关'
    vs.str = _str
    return vs
#endregion

#region 相关组合
def xiangguanzuhe_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.str = script_lib.get_string_from_element(value, '相关组合') + '相关'
    return vs
#endregion

#region 相连
def xianglian_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    bljw = script_lib.get_string_from_element(value, '被连接物')
    ljw = script_lib.get_string_from_element(value, '连接物')
    qd = script_lib.get_string_from_element(value, '连接强度')
    if ljw:
        ljw = '通过' + ljw
    vs.str = f'{bljw}{ljw}{qd}连接'
    return vs
#endregion

#region 有序连接
def youxuxlianjie_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    bljw = script_lib.get_string_from_element(value, '被连接物')
    ljw = script_lib.get_string_from_element(value, '连接物')
    qd = script_lib.get_string_from_element(value, '连接强度')
    if ljw:
        ljw = '通过' + ljw
    vs.str = f'{bljw}{ljw}{qd}有序连接'
    return vs
#endregion

#region 过程链
def guochenglian_check(value: Frame) -> bool:
    script_lib.common_check(value)
    gcxl = value.elements['过程序列']
    last_item = None
    for index, guocheng in enumerate(gcxl.value):
        if (isinstance(guocheng, Frame)) and (guocheng.name == '一般过程' or guocheng.name == '治疗'):
            if index == 0:
                if '后状态' in guocheng.elements:
                    if guocheng.elements['后状态'].is_focus:
                        last_item = guocheng.elements['后状态'].value
                    else:
                        return False
                else:
                    return False
            elif index != len(gcxl.value) - 1:
                if last_item.uuid != guocheng.elements['前状态'].value.uuid:
                    return False

                if '后状态' in guocheng.elements:
                    if guocheng.elements['后状态'].is_focus:
                        last_item = guocheng.elements['后状态'].value
                    else:
                        return False
                else:
                    return False
            else:
                if last_item.uuid != guocheng.elements['前状态'].value.uuid:
                    return False
        else:
            return False
    return True

@return_frame_if_exists(Layer.Prototype)
def guochenglian_prototype(value: Frame) -> Frame:
    frame_manager = value.frame_manager
    _gcxl = value.elements['过程序列'].apply(checked_frame_apply_script('prototype'))
    skip_start = None
    in_skip_mode = False
    _new_gcxl_value = []
    for guocheng in _gcxl.value:
        if guocheng in _gcxl.focus_items:
            if in_skip_mode:
                _new_guocheng = Frame(frame_manager, '一般过程')
                script_lib.copy_meta_data_to_new_frame(value, _new_guocheng)
                _new_guocheng.uuid = uuid4().hex

                _new_guocheng.elements['前状态'] = frame_manager.frame_name_map['一般过程']['elements']['前状态'].create_empty()
                script_lib.copy_value_to_new_element_container(skip_start.elements['前状态'], _new_guocheng.elements['前状态'])

                if '干预' in skip_start.elements:
                    _new_guocheng.elements['干预'] = frame_manager.frame_name_map['一般过程']['elements']['干预'].create_empty()
                    script_lib.copy_value_to_new_element_container(skip_start.elements['干预'], _new_guocheng.elements['干预'])

                _new_guocheng.elements['后状态'] = frame_manager.frame_name_map['一般过程']['elements']['后状态'].create_empty()
                script_lib.copy_value_to_new_element_container(guocheng.elements['后状态'], _new_guocheng.elements['后状态'])

                _new_guocheng._process = Process.Checked
                _new_guocheng.layer = Layer.Prototype
                frame_manager.marker_map_prototype[_new_guocheng.uuid] = _new_guocheng
                _new_gcxl_value.append(_new_guocheng)
                in_skip_mode = False
            else:
                _new_gcxl_value.append(guocheng)
        else:
            if not in_skip_mode:
                skip_start = guocheng
                in_skip_mode = True
    if in_skip_mode:
        _new_guocheng = Frame(frame_manager, '一般过程')
        script_lib.copy_meta_data_to_new_frame(value, _new_gcxl_value)
        _new_guocheng.layer = Layer.Prototype
        _new_guocheng.uuid = uuid4().hex

        _new_guocheng.elements['前状态'] = frame_manager.frame_name_map['一般过程']['elements']['前状态'].create_empty()
        script_lib.copy_value_to_new_element_container(skip_start.elements['前状态'], _new_guocheng.elements['前状态'])

        if '干预' in skip_start.elements:
            _new_guocheng.elements['干预'] = frame_manager.frame_name_map['一般过程']['elements']['干预'].create_empty()
            script_lib.copy_value_to_new_element_container(skip_start.elements['干预'], _new_guocheng.elements['干预'])

        _new_guocheng._process = Process.Checked
        frame_manager.marker_map_prototype[_new_guocheng.uuid] = _new_guocheng
        _new_gcxl_value.append(_new_guocheng)
        in_skip_mode = False
    if len(_new_gcxl_value) == 0:
        raise ValueError
    elif len(_new_gcxl_value) == 1:
        frame_manager.marker_map_prototype[value.uuid] = _new_gcxl_value[0]
        return _new_gcxl_value[0]
    else:
        _frame = Frame(frame_manager, '过程链')
        script_lib.copy_meta_data_to_new_frame(value, _frame)
        _frame.layer = Layer.Prototype
        _frame.elements['过程序列'] = frame_manager.frame_name_map['过程链']['elements']['过程序列'].create_empty()
        _frame.elements['过程序列'].is_focus = True
        _frame.elements['过程序列'].value = _new_gcxl_value
        _frame.elements['过程序列'].is_focus = _new_gcxl_value
        _frame._process = Process.Checked
        frame_manager.marker_map_prototype[_frame.uuid] = _frame
        return _frame

def guochenglian_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    counter = 0
    qzt: tuple[int, str] = None
    for guocheng in value.elements['过程序列'].value:
        if qzt is None:
            qzt = (counter, script_lib.get_string_from_element(guocheng, '前状态'))
            vs.append_node(*qzt)
            counter += 1
        hzt = (counter, script_lib.get_string_from_element(guocheng, '后状态'))
        gy = (counter, script_lib.get_string_from_element(guocheng, '干预'))
        vs.append_node(*hzt)
        vs[(qzt[0], hzt[0])] = gy
        counter += 1
        qzt = hzt
    return vs
#endregion

#region common 因果解释
def yinguojieshi_common_visual(frame: Frame) -> VisualStructure:
    vs = VisualStructure()
    yin = script_lib.get_string_from_element(frame, '因')
    guo = frame.elements['果'].value
    guo_vs = guo.script['visual'](guo)
    vs.nodes = guo_vs.nodes
    node_pair = list(guo_vs.edges)[0]
    edge_str = guo_vs.edges[node_pair]
    if edge_str:
        edge_str = ',因此,' + edge_str
    vs.edges[node_pair] = yin + edge_str
    return vs

#endregion

#region 过程链同构因果解释

#todo 尚未实现过程链因果可视化，只能展示果
def gcltgygjs_visual(value: Frame) -> VisualStructure:
    guo = value.elements['果'].value
    return guo.script['visual'](guo)

#endregion

#region 过程链相关因果解释

#todo 尚未实现过程链因果可视化，只能展示果
def gclxgygjs_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    guo = value.elements['果'].value
    return guo.script['visual'](guo)
#endregion

#region 伤害事件
def shanghaishijian_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    shz = script_lib.get_string_from_element(value, '伤害者')
    sshz = script_lib.get_string_from_element(value, '受伤害者')
    lx = script_lib.get_string_from_element(value, '伤害类型')
    cd = script_lib.get_string_from_element(value, '伤害程度')
    vs.str = f'{sshz}受{shz}{cd}{lx}损伤'
    return vs
#endregion

#region 使用针刺
def shiyongzhenci_visual(value: Frame) -> VisualStructure:
    vs = VisualStructure()
    vs.str = '针刺{}'.format(script_lib.get_string_from_element(value, '部位组合'))
    return vs
#endregion

DEFAULT_SCRIPT: dict[str, Callable] = {
    'check': script_lib.common_check,
    'generate': script_lib.common_generate, #do something just after check
    'primary': script_lib.common_to_primary,
    'prototype': script_lib.common_to_prototype,
    'is_same': lambda x, y: x.hash == y.hash,
    'visual': script_lib.not_implement
}

FRAME_SCRIPT: dict[str, dict[str, Callable]] = {
    '状态附加': {
        'prototype': script_lib.not_implement,
        'primary': zhuangtaifujia_primary
    },
    '基础状态附加': {
        'primary': script_lib.not_implement,
        'prototype': jichuzhuangtaifujia_prototype
    },
    '原型状态附加': {
        'primary': script_lib.not_implement,
        'prototype': script_lib.not_implement,
        'visual': yuanxingzhuangtaifujia_visual
    },
    '形成原型': {
        'primary': lambda x: x,
        'prototype': lambda x: x,
        'check': xingchengyuanxing_check,
        'visual': xingchengyuanxing_visual
    },
    '运动事件': {
        'visual': yundongshijian_visual
    },
    '中药': {
        'prototype': script_lib.common_to_prototype_allow_to_ontology,
        'visual': zhongyao_visual
    },
    '状态组合': {
        'prototype': zhuangtaizuhe_prototype,
        'visual': zhuangtaizuhe_visual
    },
    '方剂': {
        'generate': fangji_generate,
        'prototype': fangji_prototype,
        'visual': fangji_visual
    },
    '一般过程': {
        'visual': yibanguocheng_visual
    },
    '治疗': {
        'primary': zhiliao_primary,
        'prototype': script_lib.not_implement
    },
    '使用方剂': {
        'visual': shiyongfangji_visual
    },
    '状态加减': {
        'visual': zhuangtaijiajian_visual
    },
    '两者相关': {
        'visual': liangzhexiangguan_visual
    },
    '相关组合': {
        'visual': xiangguanzuhe_visual
    },
    '相连': {
        'visual': xianglian_visual
    },
    '有序连接': {
        'visual': youxuxlianjie_visual
    },
    '过程链': {
        'check': guochenglian_check,
        'prototype': guochenglian_prototype,
        'visual': guochenglian_visual 
    },
    '同构性': {},
    '过程同构因果解释': {
        'visual': yinguojieshi_common_visual
    },
    '过程相关因果解释': {
        'visual': yinguojieshi_common_visual
    },
    '建议同构因果解释': {
        'visual': yinguojieshi_common_visual
    },
    '建议相关因果解释': {
        'visual': yinguojieshi_common_visual
    },
    '过程链同构因果解释': {
        'visual': gcltgygjs_visual
    },
    '过程链相关因果解释': {
        'visual': gclxgygjs_visual
    },
    '相关同构因果解释': {},
    '伤害事件': {
        'visual': shanghaishijian_visual
    },
    '使用针刺': {
        'visual': shiyongzhenci_visual
    }
}